
from nox.lib.util import *
from se_dict import SeDict
from symbolic.instrumentation import whichBranch
python_ord = ord

def se_ord(obj):
    __foo = hasattr(obj, """__ord__""")
    if __foo:
        whichBranch(True)
        return obj.__ord__()
    else:
        whichBranch(False)
        return python_ord(obj)
ord = se_ord
from nox.lib.core import *
from nox.lib.util import *
from nox.lib.packet.ethernet import ethernet
from nox.lib.packet.packet_utils import mac_to_str, mac_to_int
from twisted.python import log
import logging
from time import time
from socket import htons
from struct import unpack
logger = logging.getLogger("""nox.coreapps.examples.pyswitch""")
inst = None
CACHE_TIMEOUT = 5

def do_l2_learning(dpid, inport, packet):
    global inst
    srcaddr = packet.src.tostring()
    __se_tmp_1 = ord(srcaddr[0])
    if (__se_tmp_1 & 1):
        whichBranch(True)
        return
    else:
        whichBranch(False)
    __se_tmp_2 = inst.st[dpid].has_key(srcaddr)
    if __se_tmp_2:
        whichBranch(True)
        dst = inst.st[dpid][srcaddr]
        if (dst[0] != inport):
            whichBranch(True)
            log.msg(((("""MAC has moved from """ + str(dst)) + """to""") + str(inport)), system="""pyswitch""")
        else:
            whichBranch(False)
            return
    else:
        whichBranch(False)
        log.msg((("""learned MAC """ + mac_to_str(packet.src)) + (""" on %d %d""" % (dpid, inport))), system="""pyswitch""")
    inst.st[dpid][srcaddr] = (inport, time(), packet)
    mac = mac_to_int(packet.src)

def forward_l2_packet(dpid, inport, packet, buf, bufid):
    dstaddr = packet.dst.tostring()
    __se_tmp_4 = ord(dstaddr[0])
    if (not (__se_tmp_4 & 1)):
        whichBranch(True)
        __se_tmp_3 = inst.st[dpid].has_key(dstaddr)
        if __se_tmp_3:
            whichBranch(True)
            prt = inst.st[dpid][dstaddr]
            if (prt[0] == inport):
                whichBranch(True)
                log.err("""**warning** learned port = inport""", system="""pyswitch""")
                inst.send_openflow(dpid, bufid, buf, openflow.OFPP_FLOOD, inport)
            else:
                whichBranch(False)
                log.msg(("""installing flow for """ + str(packet)), system="""pyswitch""")
                flow = extract_flow(packet)
                flow[core.IN_PORT] = inport
                actions = [[openflow.OFPAT_OUTPUT, [0, prt[0]]]]
                inst.install_datapath_flow(dpid, flow, CACHE_TIMEOUT, openflow.OFP_FLOW_PERMANENT, actions, bufid, openflow.OFP_DEFAULT_PRIORITY, inport, buf)
        else:
            whichBranch(False)
            inst.send_openflow(dpid, bufid, buf, openflow.OFPP_FLOOD, inport)
    else:
        whichBranch(False)
        inst.send_openflow(dpid, bufid, buf, openflow.OFPP_FLOOD, inport)

def timer_callback():
    global inst
    curtime = time()
    for dpid in inst.st.keys():
        for entry in inst.st[dpid].keys():
            if ((curtime - inst.st[dpid][entry][1]) > CACHE_TIMEOUT):
                whichBranch(True)
                log.msg(((("""timing out entry""" + mac_to_str(entry)) + str(inst.st[dpid][entry])) + (""" on switch %x""" % dpid)), system="""pyswitch""")
                inst.st[dpid].pop(entry)
            else:
                whichBranch(False)
    inst.post_callback(1, timer_callback)
    return True

def datapath_leave_callback(dpid):
    logger.info(("""Switch %x has left the network""" % dpid))
    __se_tmp_5 = inst.st.has_key(dpid)
    if __se_tmp_5:
        whichBranch(True)
        del inst.st[dpid]
    else:
        whichBranch(False)

def datapath_join_callback(dpid, stats):
    logger.info(("""Switch %x has joined the network""" % dpid))

def packet_in_callback(dpid, inport, reason, len, bufid, packet):
    if (not packet.parsed):
        whichBranch(True)
        log.msg("""Ignoring incomplete packet""", system="""pyswitch""")
    else:
        whichBranch(False)
    __se_tmp_6 = inst.st.has_key(dpid)
    if (not __se_tmp_6):
        whichBranch(True)
        log.msg(("""registering new switch %x""" % dpid), system="""pyswitch""")
        inst.st[dpid] = SeDict({})
    else:
        whichBranch(False)
    if (packet.type == ethernet.LLDP_TYPE):
        whichBranch(True)
        return CONTINUE
    else:
        whichBranch(False)
    do_l2_learning(dpid, inport, packet)
    forward_l2_packet(dpid, inport, packet, packet.arr, bufid)
    return CONTINUE

class pyswitch(Component, ):

    def __init__(self, ctxt):
        global inst
        Component.__init__(self, ctxt)
        self.st = SeDict({})
        inst = self

    def install(self):
        inst.register_for_packet_in(packet_in_callback)
        inst.register_for_datapath_leave(datapath_leave_callback)
        inst.register_for_datapath_join(datapath_join_callback)

    def getInterface(self):
        return str(pyswitch)

    def __getstate__(self):
        """ function added to have a serialized version of the app with only the necessary state """
        di = Component.__getstate__(self)
        di["""st"""] = SeDict({})
        dpids = self.st.keys()
        dpids.sort()
        for d in dpids:
            di["""st"""][d] = SeDict({})
            macs = self.st[d].keys()
            macs.sort()
            for m in macs:
                di["""st"""][d][m] = (self.st[d][m][0], 0, self.st[d][m][2])
        return di

def getFactory():

    class Factory:

        def instance(self, ctxt):
            return pyswitch(ctxt)
    return Factory()